package labrom.usage;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.CharBuffer;
import java.nio.channels.Channels;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

/**
 * A thread that sends usage data to the hosted service.
 * @author Romain Laboisse
 *
 */
public class Sender extends Thread {
	
	private Batch batch;
	private String url;
	private int itemsWritten;
	private Handler handler;
	
	/**
	 * 
	 * @param handler
	 * @param b
	 * @param url something like http://usagedata.appspot.com/v1/app1/00000000-0000-0000-0000-000000000000
	 */
	public Sender(Handler handler, Batch b, String url) {
		this.handler = handler;
		this.batch = b;
		this.url = url;
	}
	
	public int getItemsWritten() {
		return itemsWritten;
	}

	@Override
	public void run() {
		HttpClient client = new DefaultHttpClient();

		long ts = 0;
		HttpGet gett = new HttpGet(url + "/ask");
		HttpResponse resp;
		try {
			Log.d(D.TAG, "Sending GET to " + url + "/ask...");
			long start = System.currentTimeMillis();
			resp = client.execute(gett);
			StatusLine statusLine = resp.getStatusLine();
			int code = statusLine.getStatusCode();
			Log.d(D.TAG, "Got response code " + String.valueOf(code) + " in " + String.valueOf(System.currentTimeMillis() - start) + "ms");
			if(code < 300)
				ts = Long.parseLong(readResponse(resp.getEntity()));
			else
				Log.e(D.TAG, "Reason for bad response code is: " + statusLine.getReasonPhrase());
			Log.i(D.TAG, "Will send usage data since timestamp " + String.valueOf(ts));
		} catch (ClientProtocolException e) {
			logException(e);
		} catch (IOException e) {
			logException(e);
		} catch (NumberFormatException e) {
			logException(e);
		}
		
		int removed = batch.stripBeforeTimestamp(ts);
		if(removed > 0)
			Log.i(D.TAG, "Removed " + removed + " items because they are older than the youngest item in database");
		
		HttpPost post = new HttpPost(url + "/udis");
		
		try {
			HttpEntity payload = batch.createHttpEntity(ts);
			if(payload == null) {
				Log.i(D.TAG, "Nothing to send");
			} else {
				post.setEntity(payload);
				Log.d(D.TAG, "Sending POST to " + url + "/udis...");
				long start = System.currentTimeMillis();
				resp = client.execute(post);
				StatusLine statusLine = resp.getStatusLine();
				int code = statusLine.getStatusCode();
				Log.d(D.TAG, "Got response code " + String.valueOf(code) + " in " + String.valueOf(System.currentTimeMillis() - start) + "ms");
				if(code < 300)
					itemsWritten = Integer.parseInt(readResponse(resp.getEntity()));
				else
					Log.e(D.TAG, "Reason for bad response code is: " + statusLine.getReasonPhrase());
				logResult(itemsWritten);
			}
			
		} catch(ClientProtocolException e) {
			logException(e);
		} catch(IOException e) {
			logException(e);
		} catch (NumberFormatException e) {
			logException(e);
		}
		
		// Send message back to main thread, with number of items saved
		handler.sendMessage(Message.obtain(handler, itemsWritten)); 
	}

	private void logResult(int itemsWritten) {
		StringBuilder sb = new StringBuilder();
		sb.append("Successfully wrote ")
		.append(String.valueOf(itemsWritten))
		.append(" items out of ").append(String.valueOf(batch.size()));
		Log.i(D.TAG, sb.toString());
	}

	private void logException(Exception e) {
		StringBuilder sb = new StringBuilder();
		sb.append(e.getClass().getName()).append(" - ").append(e.getMessage());
		if(e.getCause() != null) {
			sb.append("; Caused by: ");
			sb.append(e.getCause().getClass().getName());
			sb.append(" (").append(e.getCause().getMessage()).append(")");
		}
	}

	
	/**
	 * Reads the HTTP response body string.
	 * @param e
	 * @return
	 * @throws IllegalStateException
	 * @throws IOException
	 */
	private String readResponse(HttpEntity e) throws IllegalStateException, IOException {
		InputStream in = e.getContent();
		Reader r = Channels.newReader(Channels.newChannel(in), "UTF-8");
		CharBuffer chars = CharBuffer.allocate(30);
		while(r.read(chars) >= 0) {}
		e.consumeContent();
		chars.flip();
		return chars.toString();
	}
}